﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NetworkManagement;
using DataObject = LorraineSec.DatabaseLib.DataObjects;

namespace LorraineSec.MessageLib.Messages
{
    public sealed class DeviceListResponse : EncryptedMessage
    {
        #region Format Constants
        /*
         * Format:
         *         2           4      1          4              1           1          1        x      y      z
         * | # DeviceLists | { ID | LibID | CompartmentID | NameLength | TypeLen | SerialLen | Name | Type |Serial }
         * 
         */

        //lengths
        private const int ENC_NUMDEVICELISTS_LENGTH = 2;
        private const int SET_ID_LENGTH = 4;
        private const int SET_LIBID_LENGTH = 1;
        private const int SET_COMPARTMENTID_LENGTH = 4;
        private const int SET_NAMELEN_LENGTH = 1;
        private const int SET_TYPELEN_LENGTH = 1;
        private const int SET_SERIALLEN_LENGTH = 1;

        //offsets
        private const int ENC_NUMDEVICELISTS_OFFSET = 0;
        private const int SET_ID_OFFSET = 0;
        private const int SET_LIBID_OFFSET = SET_ID_OFFSET + SET_ID_LENGTH;
        private const int SET_COMPARTMENTID_OFFSET = SET_ID_OFFSET + SET_ID_LENGTH;
        private const int SET_NAMELEN_OFFSET = SET_COMPARTMENTID_OFFSET + SET_COMPARTMENTID_LENGTH;
        private const int SET_TYPELEN_OFFSET = SET_NAMELEN_OFFSET + SET_NAMELEN_LENGTH;
        private const int SET_SERIALLEN_OFFSET = SET_TYPELEN_OFFSET + SET_TYPELEN_LENGTH;
        private const int SET_NAME_OFFSET = SET_SERIALLEN_OFFSET + SET_SERIALLEN_LENGTH;

        //known lengths
        private const int ENC_KNOWN_LENGTH = ENC_NUMDEVICELISTS_LENGTH;
        private const int SET_KNOWN_LENGTH = SET_ID_LENGTH + SET_LIBID_LENGTH + SET_COMPARTMENTID_LENGTH + SET_NAMELEN_LENGTH + SET_TYPELEN_LENGTH + SET_SERIALLEN_LENGTH;
        #endregion

        public struct Device
        {
            public Device(uint _id,byte _libID,uint _compID, string _name, string _type, string _serial)
            {
                ID = _id;
                LibID = _libID;
                CompartmentID = _compID;
                Name = _name;
                Type = _type;
                Serial = _serial;
            }
            public static implicit operator Device(DataObject.Device obj)
            {
                return new Device(obj.ID, obj.LibID, obj.CompartmentID, obj.Name, obj.Type, obj.Serial);
            }

            public uint ID;
            public byte LibID;
            public uint CompartmentID;
            public string Name;
            public string Type;
            public string Serial;
        }

        public List<Device> Devices;

        public DeviceListResponse()
        {
            init();
        }

        public DeviceListResponse(byte[] bytes)
            : base(bytes)
        {
            init();
            decode();
        }

        private void init()
        {
            MessageID = (ushort)LorraineSecMessageFactory.MessageID.COMPARTMENT_LIST_RESPONSE;
            Devices = new List<Device>();
        }

        public override void decrypt(CryptoManager crypt)
        {
            base.decrypt(crypt);
            //Decode byte array
            //Get number of Device Lists
            ushort numDevLists = Bytes.getUInt16(encData, ENC_NUMDEVICELISTS_OFFSET);
            //Set offset variable
            int offset = ENC_NUMDEVICELISTS_OFFSET + ENC_NUMDEVICELISTS_LENGTH;
            for (int i = 0; i < numDevLists; ++i)
            {
                //Get Device List ID
                ushort id = Bytes.getUInt16(encData, offset);
                offset += SET_ID_LENGTH;
                //Get Lib ID
                byte LibID = Bytes.getByte(encData, offset);
                offset += SET_LIBID_LENGTH;
                //Get Compartment ID
                uint CompartmentID = Bytes.getUInt32(encData, offset);
                offset += SET_COMPARTMENTID_LENGTH;
                //Get length of Device List name
                int nameLen = Bytes.getByte(encData, offset);
                offset += SET_NAMELEN_LENGTH;
                //Get length of Type
                int typeLen = Bytes.getByte(encData, offset);
                offset += SET_TYPELEN_LENGTH;
                //Get length of Serial
                int serialLen = Bytes.getByte(encData, offset);
                offset += SET_SERIALLEN_LENGTH;
                //Get Device List name
                string name = Bytes.getString(encData, offset, nameLen);
                offset += nameLen;
                //Get Device List type
                string type = Bytes.getString(encData, offset, typeLen);
                offset += typeLen;
                //Get Device List Serial
                string serial = Bytes.getString(encData, offset, serialLen);
                offset += serialLen;
                //Add Device List to dictionary
                Devices.Add(new Device(id, LibID, CompartmentID, name, type, serial));
            }
        }

        public override void encrypt(CryptoManager crypt)
        {
            //Encode byte array
            //Precount byte array allocation size
            int strAllocSize = 0;
            
            foreach ( Device str in Devices)
            {
                strAllocSize += str.Name.Length;
                strAllocSize += str.Type.Length;
                strAllocSize += str.Serial.Length;
            }
            
            //Allocate memory for encrypion field
            encData = new byte[ENC_KNOWN_LENGTH + Devices.Count * SET_KNOWN_LENGTH + strAllocSize];

            //Set the number of Device Lists
            Bytes.setValue(encData, ENC_NUMDEVICELISTS_OFFSET, (ushort)Devices.Count);
            //Set offset variable
            int offset = ENC_NUMDEVICELISTS_OFFSET + ENC_NUMDEVICELISTS_LENGTH;
            foreach (Device dev in Devices)
            {                
                //Set Device List ID
                Bytes.setValue(encData, offset, dev.ID);
                offset += SET_ID_LENGTH;
                //Set Lib ID
                Bytes.setValue(encData, offset, dev.LibID);
                offset += SET_LIBID_LENGTH;
                //Set Compartment ID
                Bytes.setValue(encData, offset, dev.CompartmentID);
                offset += SET_COMPARTMENTID_LENGTH;
                //Set Device List name length
                Bytes.setValue(encData, offset, (byte)dev.Name.Length);
                offset += SET_NAMELEN_LENGTH;
                //Set type length
                Bytes.setValue(encData, offset, (byte)dev.Type.Length);
                offset += SET_TYPELEN_LENGTH;
                //Set serial length
                Bytes.setValue(encData, offset, (byte)dev.Serial.Length);
                offset += SET_SERIALLEN_LENGTH;
                //Set Device List name
                Bytes.setValue(encData, offset, dev.Name);
                offset += dev.Name.Length;
                //Set Type
                Bytes.setValue(encData, offset, dev.Type);
                offset += dev.Type.Length;
                //Set Serial
                Bytes.setValue(encData, offset, dev.Serial);
                offset += dev.Serial.Length;
            }
            base.encrypt(crypt);
        }
    }
}
